package ving.mvc;

import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * Created by liuweijie on 2019-12-07.
 */
public class MvcBeanFactory {

    private ApplicationContext applicationContext;

    public MvcBeanFactory(ApplicationContext applicationContext){
        Assert.notNull(applicationContext,"argument 'applicationContext' must not be null");
        this.applicationContext = applicationContext;
        loadApiFromSpringBeans();

    }

    // API 接口住的地方
    private HashMap<String,MvcBean> apiMap = new HashMap();

    public MvcBean getMvcBean(String apiName){
        return apiMap.get(apiName);

    }

    private void loadApiFromSpringBeans() {

        apiMap.clear();
        // ioc 所有bean
        //spring ioc 扫描
        String[] names = applicationContext.getBeanDefinitionNames();
        Class<?> type;
        for (String name : names) {
            // 只需要拿类型的可以了？
            type = applicationContext.getType(name);
            Method[] declaredMethods = type.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                MvcMapping annotation = declaredMethod.getAnnotation(MvcMapping.class);
                if (annotation!=null) {
                    addApiItem(annotation,name,declaredMethod);
                }
            }
        }

    }

    private void addApiItem(MvcMapping mvcMapping, String beanName, Method method) {
        MvcBean apiRun = new MvcBean();
        apiRun.apiName = mvcMapping.value();
        apiRun.targetMethod = method;
        apiRun.targetName = beanName;
        apiRun.context = this.applicationContext;
        apiMap.put(mvcMapping.value(),apiRun);

    }

    // 这里为什么会弄成public static?
    public static class MvcBean {
        String apiName; // bit.api.user.getUser

        String targetName;// ioc bean 名称

        Object target; // UserServiceImpl 实例

        Method targetMethod;// 目标方法 getUser
        ApplicationContext context;

        public Object run(Object... args)throws IllegalAccessException,IllegalArgumentException, InvocationTargetException{
            // 懒加载
            if(target ==null){
                // spring ioc 容器里面去服务bean 比如GoodsServiceImpl
                target = context.getBean(targetName);
            }
            return targetMethod.invoke(target,args);
        }

        public Method getTargetMethod() {
            return targetMethod;
        }
    }
}
